This notebook aims to provide some examples of dynamical systems demonstrating chaotical behaviour. We'll start from simple reccurent equations and go forward to space-time distributed systems.
Most examples are taken from the book "Dynamical chaos" by S.Kuznetsov available in Russian.
In [1]:
%pylab
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
#import matplotlib
#matplotlib.style.use('ggplot')
In [2]:
def iterator(f, x0=0, inf=1000):
"""
iterator function returns iterator for given function f.
Parameters:
f iterating function of one argument
x0 initial condition
inf maximum next absolute value of a variable when iteration stops
"""
x = x0 # x - iteration value, i - counter
while abs(x) < inf:
yield x
x = f(x)
assert [x for x in iterator(lambda x: x+2, x0=2, inf=15)] == [2, 4, 6, 8, 10, 12, 14]
In [3]:
def take(it, n=100, skip=10):
"""
take takes n points from iterator it skipping first skip points.
Parameters:
it iterator
n number of results to return
skip number of steps to skip
"""
i = 0
while i < skip:
try:
it.next()
i += 1
except StopIteration:
return []
i = 0
result = []
while i < n:
try:
result.append(it.next())
i += 1
except StopIteration:
return result
return result
assert take(iterator(lambda x: x+2, x0=1, inf=10000), n=5, skip=5) == [11, 13, 15, 17, 19]
In [4]:
def diagram_points(xs):
"""
diagram_points takes list of numbers and returns a list
of tuples where each tuple corresponds to a point
on iterative diagram.
"""
#result = [(xs[0], 0)]
result = []
for x, y in zip(xs, xs[1:]):
result.append((x,x))
result.append((x,y))
return result
#assert diagram_points([1,2,3]) == [(1,0), (1,1), (1,2), (2,2), (2,3)]
assert diagram_points([1,2,3]) == [(1,1), (1,2), (2,2), (2,3)]
In [5]:
def linspace(start=1.0, stop=10.0, step=1.0):
"""
linspace returns list of linear space steps from start to stop
devided by step
"""
return [start+i*step for i in range(int((stop-start)/step)+1)]
assert linspace(1,3,0.5) == [1, 1.5, 2, 2.5, 3]
assert linspace() == [1,2,3,4,5,6,7,8,9,10]
In [6]:
# boundary is a function which returns boundary curve for given function
boundary = lambda f, limits: pd.DataFrame([(x, f(x)) for x in linspace(limits[0], limits[1], 0.001)])
In [7]:
# let's draw cobweb plot diagram x_{n+1} over x_n of sawtooth map
# along with evolution of x_n over n
def cobweb_plot(xs, limits=[0,1], title='Plot', *dfs):
fig, axes = plt.subplots(1, 2, figsize=(15, 6));
plt.subplots_adjust(wspace=0.5, hspace=0.5);
pd.DataFrame(zip(limits,limits)).plot(x=0, y=1, ax=axes[0], xlim=limits, ylim=limits, legend=False, color='k')
for df in dfs:
df.plot(x=0, y=1, ax=axes[0], legend=False, color='k')
pd.DataFrame(diagram_points(xs), columns=('n', 'n1')).plot(x='n', y='n1', style='o-', ax=axes[0], legend=False)
pd.DataFrame(xs).plot(style='o-', ax=axes[1], legend=False)
axes[0].set_xlabel('x_n')
axes[0].set_ylabel('x_n+1')
axes[1].set_xlabel('n')
axes[1].set_ylabel('x_n')
In [8]:
#from math import trunc
assert trunc(1.5) == 1.0
assert trunc(12.59) == 12.0
assert trunc(1) == 1.0
In [9]:
sawtooth = lambda x: round(2*x-trunc(2*x),8)
In [10]:
sawtooth_borders = [pd.DataFrame([(0,0),(0.5,1)]), pd.DataFrame([(0.5,0),(1,1)])]
for x0 in [0.4, 0.41, 0.42, 0.43]:
cobweb_plot(take(iterator(sawtooth, x0=x0), n=30, skip=500), [0,1], *sawtooth_borders)
In [11]:
logistic = lambda k, x: k*x*(1-x)
In [12]:
limits = [0,1]
for k in [2.8, 3.2, 3.5, 3.9]:
l = lambda x: logistic(k, x)
cobweb_plot(take(iterator(l, x0=0.1), n=30, skip=500), limits, 'plot', boundary(l, limits))
In [13]:
# let's plot the bifurcation diagram
dots = []
for k in linspace(2.5, 4, 0.001):
for dot in set(take(iterator(lambda x: logistic(k, x), x0=0.5), n=50, skip=500)):
dots.append((k, dot))
df = pd.DataFrame(dots, columns=('k', 'xs'))
df.plot(x='k', y='xs', kind='scatter', style='.', figsize=(16,12), s=1, xlim=[2.5,4], ylim=[0,1])
Out[13]: